// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///| Time zone.
struct Zone {
  id : String
  offsets : Array[ZoneOffset]
  transitions : Array[(Int64, Int)] // (timestamp, offset_index)
} derive(Eq)

///| UTC time zone.
pub let utc_zone : Zone = { id: "UTC", offsets: [utc_offset], transitions: [] }

///| Creates a time zone with fixed offset from time zone id and offset seconds.
pub fn fixed_zone(
  id : String,
  offset_seconds : Int,
  abbrev~ : String = "",
  dst~ : Bool = false,
) -> Zone raise Error {
  let offset = ZoneOffset::from_seconds(offset_seconds, abbrev~, dst~)
  Zone::{ id, offsets: [offset], transitions: [] }
}

///| Checks if this zone only has one offset.
pub fn is_fixed(self : Zone) -> Bool {
  self.offsets.length() == 1
}

///| Returns the fixed zone id (e.g. "UTC", "America/Los_Angeles", etc.)
pub fn Zone::to_string(self : Zone) -> String {
  self.id
}

///|
pub impl Show for Zone with output(self : Zone, logger : &Logger) -> Unit {
  logger.write_string(self.to_string())
}

///|
fn lookup_offset(self : Zone, secs : Int64) -> ZoneOffset {
  if self.offsets.length() == 0 {
    return utc_offset
  }
  if self.is_fixed() {
    return self.offsets[0]
  }
  let mut last_offset = 0
  for i in 0..<self.transitions.length() {
    let (timestamp, offset_index) = self.transitions[i]
    if timestamp <= secs {
      last_offset = offset_index
    } else {
      break
    }
  }
  return self.offsets[last_offset]
}

///|
const TZIF_MAGIC_HEADER : Int = 0x545A6966

///|
const TZIF2_VERSION = 50

///|
const TZIF2_HEADER_SIZE : Int = 44

///| Creates a time zone from TZif2 data.
pub fn Zone::from_tzif2(
  id : String,
  data : FixedArray[Byte],
) -> Zone raise Error {
  let magic = to_int_be(data, 0)
  if magic != TZIF_MAGIC_HEADER {
    raise Failure("invalid TZif magic: \{magic}, want \{TZIF_MAGIC_HEADER}")
  }
  let version = data[4].to_int()
  if version != TZIF2_VERSION {
    raise Failure(
      "unsupported TZif2 version: \{version}, want \{TZIF2_VERSION}",
    )
  }

  //
  let timecnt = to_int_be(data, 32)
  let typecnt = to_int_be(data, 36)
  let transition_indices_offset = TZIF2_HEADER_SIZE + timecnt * 4
  let ttinfo_offset = transition_indices_offset + timecnt
  let abbrev_offset = ttinfo_offset + typecnt * 6
  let transitions = Array::makei(timecnt, fn(i) {
    let offset = TZIF2_HEADER_SIZE + i * 4
    let transition_time = to_int_be(data, offset).to_int64()
    (transition_time, data[transition_indices_offset + i].to_int())
  })
  let offsets = Array::new(capacity=typecnt)
  for i in 0..<typecnt {
    let offset = ttinfo_offset + i * 6
    let dst = data[offset + 4] != 0
    let abbrev_index = abbrev_offset + data[offset + 5].to_int()
    let abbrev = ascii_to_string(data, abbrev_index)
    offsets.push(
      ZoneOffset::from_seconds(to_int_be(data, offset), abbrev~, dst~),
    )
  }
  { id, offsets, transitions }
}

///| Helper function to convert bytes to 32-bit Int (big-endian)
fn to_int_be(b : FixedArray[Byte], start : Int) -> Int {
  (b[start].to_int() << 24) |
  (b[start + 1].to_int() << 16) |
  (b[start + 2].to_int() << 8) |
  b[start + 3].to_int()
}

///| Helper function to convert a null-terminated ASCII string to a MoonBit String.
fn ascii_to_string(b : FixedArray[Byte], start : Int) -> String {
  let result = StringBuilder::new()
  let mut start = start
  while b[start] != 0 {
    result.write_char(Int::unsafe_to_char(b[start].to_int()))
    start += 1
  }
  result.to_string()
}

///|
test "Zone::from_tzif2" {
  let data : FixedArray[Byte] = [
    // magic number
    84, 90, 105, 102,
    // version
     50,
    // unused
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    // ttisutcnt = 6
     0, 0, 0, 6,
    // ttisstdcnt = 6
     0, 0, 0, 6,
    // leapcnt = 0
     0, 0, 0, 0,
    // timecnt = 186
     0, 0, 0, 186,
    // typecnt = 6
     0, 0, 0, 6,
    // charcnt = 20
     0, 0, 0, 20,
    // data[44..]- transition timestamps
     128, 0, 0, 0, 158, 166, 72, 160, 159, 187, 21, 144, 160, 134, 42, 160, 161,
    154, 247, 144, 203, 137, 26, 160, 210, 35, 244, 112, 210, 97, 38, 16, 214, 254,
    116, 92, 216, 128, 173, 144, 218, 254, 195, 144, 219, 192, 144, 16, 220, 222,
    165, 144, 221, 169, 172, 144, 222, 190, 135, 144, 223, 137, 142, 144, 224, 158,
    105, 144, 225, 105, 112, 144, 226, 126, 75, 144, 227, 73, 82, 144, 228, 94, 45,
    144, 229, 41, 52, 144, 230, 71, 74, 16, 231, 18, 81, 16, 232, 39, 44, 16, 232,
    242, 51, 16, 234, 7, 14, 16, 234, 210, 21, 16, 235, 230, 240, 16, 236, 177, 247,
    16, 237, 198, 210, 16, 238, 145, 217, 16, 239, 175, 238, 144, 240, 113, 187,
    16, 241, 143, 208, 144, 242, 127, 193, 144, 243, 111, 178, 144, 244, 95, 163,
    144, 245, 79, 148, 144, 246, 63, 133, 144, 247, 47, 118, 144, 248, 40, 162, 16,
    249, 15, 88, 144, 250, 8, 132, 16, 250, 248, 131, 32, 251, 232, 102, 16, 252,
    216, 101, 32, 253, 200, 72, 16, 254, 184, 71, 32, 255, 168, 42, 16, 0, 152, 41,
    32, 1, 136, 12, 16, 2, 120, 11, 32, 3, 113, 40, 144, 4, 97, 39, 160, 5, 81, 10,
    144, 6, 65, 9, 160, 7, 48, 236, 144, 7, 141, 67, 160, 9, 16, 206, 144, 9, 173,
    191, 32, 10, 240, 176, 144, 11, 224, 175, 160, 12, 217, 205, 16, 13, 192, 145,
    160, 14, 185, 175, 16, 15, 169, 174, 32, 16, 153, 145, 16, 17, 137, 144, 32,
    18, 121, 115, 16, 19, 105, 114, 32, 20, 89, 85, 16, 21, 73, 84, 32, 22, 57, 55,
    16, 23, 41, 54, 32, 24, 34, 83, 144, 25, 9, 24, 32, 26, 2, 53, 144, 26, 242,
    52, 160, 27, 226, 23, 144, 28, 210, 22, 160, 29, 193, 249, 144, 30, 177, 248,
    160, 31, 161, 219, 144, 32, 118, 43, 32, 33, 129, 189, 144, 34, 86, 13, 32, 35,
    106, 218, 16, 36, 53, 239, 32, 37, 74, 188, 16, 38, 21, 209, 32, 39, 42, 158,
    16, 39, 254, 237, 160, 41, 10, 128, 16, 41, 222, 207, 160, 42, 234, 98, 16, 43,
    190, 177, 160, 44, 211, 126, 144, 45, 158, 147, 160, 46, 179, 96, 144, 47, 126,
    117, 160, 48, 147, 66, 144, 49, 103, 146, 32, 50, 115, 36, 144, 51, 71, 116,
    32, 52, 83, 6, 144, 53, 39, 86, 32, 54, 50, 232, 144, 55, 7, 56, 32, 56, 28,
    5, 16, 56, 231, 26, 32, 57, 251, 231, 16, 58, 198, 252, 32, 59, 219, 201, 16,
    60, 176, 24, 160, 61, 187, 171, 16, 62, 143, 250, 160, 63, 155, 141, 16, 64,
    111, 220, 160, 65, 132, 169, 144, 66, 79, 190, 160, 67, 100, 139, 144, 68, 47,
    160, 160, 69, 68, 109, 144, 69, 243, 211, 32, 71, 45, 138, 16, 71, 211, 181,
    32, 73, 13, 108, 16, 73, 179, 151, 32, 74, 237, 78, 16, 75, 156, 179, 160, 76,
    214, 106, 144, 77, 124, 149, 160, 78, 182, 76, 144, 79, 92, 119, 160, 80, 150,
    46, 144, 81, 60, 89, 160, 82, 118, 16, 144, 83, 28, 59, 160, 84, 85, 242, 144,
    84, 252, 29, 160, 86, 53, 212, 144, 86, 229, 58, 32, 88, 30, 241, 16, 88, 197,
    28, 32, 89, 254, 211, 16, 90, 164, 254, 32, 91, 222, 181, 16, 92, 132, 224, 32,
    93, 190, 151, 16, 94, 100, 194, 32, 95, 158, 121, 16, 96, 77, 222, 160, 97, 135,
    149, 144, 98, 45, 192, 160, 99, 103, 119, 144, 100, 13, 162, 160, 101, 71, 89,
    144, 101, 237, 132, 160, 103, 39, 59, 144, 103, 205, 102, 160, 105, 7, 29, 144,
    105, 173, 72, 160, 106, 230, 255, 144, 107, 150, 101, 32, 108, 208, 28, 16, 109,
    118, 71, 32, 110, 175, 254, 16, 111, 86, 41, 32, 112, 143, 224, 16, 113, 54,
    11, 32, 114, 111, 194, 16, 115, 21, 237, 32, 116, 79, 164, 16, 116, 255, 9, 160,
    118, 56, 192, 144, 118, 222, 235, 160, 120, 24, 162, 144, 120, 190, 205, 160,
    121, 248, 132, 144, 122, 158, 175, 160, 123, 216, 102, 144, 124, 126, 145, 160,
    125, 184, 72, 144, 126, 94, 115, 160, 127, 152, 42, 144,
    // offset indices per transition timestamp
     5, 1, 2, 1, 2, 3, 4, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
    1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
    1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
    1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
    1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
    1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
    1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
    1, 2, 1, 2,
    // data[974..]: offset 0: 4 for offset, 1 for isdst, 1 for abbreviation
     255, 255, 145, 38, 0, 0,
    // data[980..]: offset 1:
     255, 255, 157, 144, 1, 4,
    // data[986..]: offset 2:
     255, 255, 143, 128, 0, 8,
    // data[992..]: offset 3:
     255, 255, 157, 144, 1, 12,
    // data[998..]: offset 4:
     255, 255, 157, 144, 1, 16,
    // data[1004..]: offset 5:
     255, 255, 143, 128, 0, 8,
    // data[1008..]: abbrev 0: "LMT\0"
     76, 77, 84, 0,
    // data[1012..]: abbrev 1: "PDT\0"
     80, 68, 84, 0,
    // data[1016..]: abbrev 2: "PST\0"
     80, 83, 84, 0,
    // data[1020..]: abbrev 3: "PWT\0"
     80, 87, 84, 0,
    // data[1024..]: abbrev 4: "PPT\0"
     80, 80, 84, 0,
    // data[1028..]
     0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 84, 90, 105, 102, 50, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 186,
    0, 0, 0, 6, 0, 0, 0, 20, 255, 255, 255, 255, 94, 4, 26, 192, 255, 255, 255, 255,
    158, 166, 72, 160, 255, 255, 255, 255, 159, 187, 21, 144, 255, 255, 255, 255,
    160, 134, 42, 160, 255, 255, 255, 255, 161, 154, 247, 144, 255, 255, 255, 255,
    203, 137, 26, 160, 255, 255, 255, 255, 210, 35, 244, 112, 255, 255, 255, 255,
    210, 97, 38, 16, 255, 255, 255, 255, 214, 254, 116, 92, 255, 255, 255, 255, 216,
    128, 173, 144, 255, 255, 255, 255, 218, 254, 195, 144, 255, 255, 255, 255, 219,
    192, 144, 16, 255, 255, 255, 255, 220, 222, 165, 144, 255, 255, 255, 255, 221,
    169, 172, 144, 255, 255, 255, 255, 222, 190, 135, 144, 255, 255, 255, 255, 223,
    137, 142, 144, 255, 255, 255, 255, 224, 158, 105, 144, 255, 255, 255, 255, 225,
    105, 112, 144, 255, 255, 255, 255, 226, 126, 75, 144, 255, 255, 255, 255, 227,
    73, 82, 144, 255, 255, 255, 255, 228, 94, 45, 144, 255, 255, 255, 255, 229, 41,
    52, 144, 255, 255, 255, 255, 230, 71, 74, 16, 255, 255, 255, 255, 231, 18, 81,
    16, 255, 255, 255, 255, 232, 39, 44, 16, 255, 255, 255, 255, 232, 242, 51, 16,
    255, 255, 255, 255, 234, 7, 14, 16, 255, 255, 255, 255, 234, 210, 21, 16, 255,
    255, 255, 255, 235, 230, 240, 16, 255, 255, 255, 255, 236, 177, 247, 16, 255,
    255, 255, 255, 237, 198, 210, 16, 255, 255, 255, 255, 238, 145, 217, 16, 255,
    255, 255, 255, 239, 175, 238, 144, 255, 255, 255, 255, 240, 113, 187, 16, 255,
    255, 255, 255, 241, 143, 208, 144, 255, 255, 255, 255, 242, 127, 193, 144, 255,
    255, 255, 255, 243, 111, 178, 144, 255, 255, 255, 255, 244, 95, 163, 144, 255,
    255, 255, 255, 245, 79, 148, 144, 255, 255, 255, 255, 246, 63, 133, 144, 255,
    255, 255, 255, 247, 47, 118, 144, 255, 255, 255, 255, 248, 40, 162, 16, 255,
    255, 255, 255, 249, 15, 88, 144, 255, 255, 255, 255, 250, 8, 132, 16, 255, 255,
    255, 255, 250, 248, 131, 32, 255, 255, 255, 255, 251, 232, 102, 16, 255, 255,
    255, 255, 252, 216, 101, 32, 255, 255, 255, 255, 253, 200, 72, 16, 255, 255,
    255, 255, 254, 184, 71, 32, 255, 255, 255, 255, 255, 168, 42, 16, 0, 0, 0, 0,
    0, 152, 41, 32, 0, 0, 0, 0, 1, 136, 12, 16, 0, 0, 0, 0, 2, 120, 11, 32, 0, 0,
    0, 0, 3, 113, 40, 144, 0, 0, 0, 0, 4, 97, 39, 160, 0, 0, 0, 0, 5, 81, 10, 144,
    0, 0, 0, 0, 6, 65, 9, 160, 0, 0, 0, 0, 7, 48, 236, 144, 0, 0, 0, 0, 7, 141, 67,
    160, 0, 0, 0, 0, 9, 16, 206, 144, 0, 0, 0, 0, 9, 173, 191, 32, 0, 0, 0, 0, 10,
    240, 176, 144, 0, 0, 0, 0, 11, 224, 175, 160, 0, 0, 0, 0, 12, 217, 205, 16, 0,
    0, 0, 0, 13, 192, 145, 160, 0, 0, 0, 0, 14, 185, 175, 16, 0, 0, 0, 0, 15, 169,
    174, 32, 0, 0, 0, 0, 16, 153, 145, 16, 0, 0, 0, 0, 17, 137, 144, 32, 0, 0, 0,
    0, 18, 121, 115, 16, 0, 0, 0, 0, 19, 105, 114, 32, 0, 0, 0, 0, 20, 89, 85, 16,
    0, 0, 0, 0, 21, 73, 84, 32, 0, 0, 0, 0, 22, 57, 55, 16, 0, 0, 0, 0, 23, 41, 54,
    32, 0, 0, 0, 0, 24, 34, 83, 144, 0, 0, 0, 0, 25, 9, 24, 32, 0, 0, 0, 0, 26, 2,
    53, 144, 0, 0, 0, 0, 26, 242, 52, 160, 0, 0, 0, 0, 27, 226, 23, 144, 0, 0, 0,
    0, 28, 210, 22, 160, 0, 0, 0, 0, 29, 193, 249, 144, 0, 0, 0, 0, 30, 177, 248,
    160, 0, 0, 0, 0, 31, 161, 219, 144, 0, 0, 0, 0, 32, 118, 43, 32, 0, 0, 0, 0,
    33, 129, 189, 144, 0, 0, 0, 0, 34, 86, 13, 32, 0, 0, 0, 0, 35, 106, 218, 16,
    0, 0, 0, 0, 36, 53, 239, 32, 0, 0, 0, 0, 37, 74, 188, 16, 0, 0, 0, 0, 38, 21,
    209, 32, 0, 0, 0, 0, 39, 42, 158, 16, 0, 0, 0, 0, 39, 254, 237, 160, 0, 0, 0,
    0, 41, 10, 128, 16, 0, 0, 0, 0, 41, 222, 207, 160, 0, 0, 0, 0, 42, 234, 98, 16,
    0, 0, 0, 0, 43, 190, 177, 160, 0, 0, 0, 0, 44, 211, 126, 144, 0, 0, 0, 0, 45,
    158, 147, 160, 0, 0, 0, 0, 46, 179, 96, 144, 0, 0, 0, 0, 47, 126, 117, 160, 0,
    0, 0, 0, 48, 147, 66, 144, 0, 0, 0, 0, 49, 103, 146, 32, 0, 0, 0, 0, 50, 115,
    36, 144, 0, 0, 0, 0, 51, 71, 116, 32, 0, 0, 0, 0, 52, 83, 6, 144, 0, 0, 0, 0,
    53, 39, 86, 32, 0, 0, 0, 0, 54, 50, 232, 144, 0, 0, 0, 0, 55, 7, 56, 32, 0, 0,
    0, 0, 56, 28, 5, 16, 0, 0, 0, 0, 56, 231, 26, 32, 0, 0, 0, 0, 57, 251, 231, 16,
    0, 0, 0, 0, 58, 198, 252, 32, 0, 0, 0, 0, 59, 219, 201, 16, 0, 0, 0, 0, 60, 176,
    24, 160, 0, 0, 0, 0, 61, 187, 171, 16, 0, 0, 0, 0, 62, 143, 250, 160, 0, 0, 0,
    0, 63, 155, 141, 16, 0, 0, 0, 0, 64, 111, 220, 160, 0, 0, 0, 0, 65, 132, 169,
    144, 0, 0, 0, 0, 66, 79, 190, 160, 0, 0, 0, 0, 67, 100, 139, 144, 0, 0, 0, 0,
    68, 47, 160, 160, 0, 0, 0, 0, 69, 68, 109, 144, 0, 0, 0, 0, 69, 243, 211, 32,
    0, 0, 0, 0, 71, 45, 138, 16, 0, 0, 0, 0, 71, 211, 181, 32, 0, 0, 0, 0, 73, 13,
    108, 16, 0, 0, 0, 0, 73, 179, 151, 32, 0, 0, 0, 0, 74, 237, 78, 16, 0, 0, 0,
    0, 75, 156, 179, 160, 0, 0, 0, 0, 76, 214, 106, 144, 0, 0, 0, 0, 77, 124, 149,
    160, 0, 0, 0, 0, 78, 182, 76, 144, 0, 0, 0, 0, 79, 92, 119, 160, 0, 0, 0, 0,
    80, 150, 46, 144, 0, 0, 0, 0, 81, 60, 89, 160, 0, 0, 0, 0, 82, 118, 16, 144,
    0, 0, 0, 0, 83, 28, 59, 160, 0, 0, 0, 0, 84, 85, 242, 144, 0, 0, 0, 0, 84, 252,
    29, 160, 0, 0, 0, 0, 86, 53, 212, 144, 0, 0, 0, 0, 86, 229, 58, 32, 0, 0, 0,
    0, 88, 30, 241, 16, 0, 0, 0, 0, 88, 197, 28, 32, 0, 0, 0, 0, 89, 254, 211, 16,
    0, 0, 0, 0, 90, 164, 254, 32, 0, 0, 0, 0, 91, 222, 181, 16, 0, 0, 0, 0, 92, 132,
    224, 32, 0, 0, 0, 0, 93, 190, 151, 16, 0, 0, 0, 0, 94, 100, 194, 32, 0, 0, 0,
    0, 95, 158, 121, 16, 0, 0, 0, 0, 96, 77, 222, 160, 0, 0, 0, 0, 97, 135, 149,
    144, 0, 0, 0, 0, 98, 45, 192, 160, 0, 0, 0, 0, 99, 103, 119, 144, 0, 0, 0, 0,
    100, 13, 162, 160, 0, 0, 0, 0, 101, 71, 89, 144, 0, 0, 0, 0, 101, 237, 132, 160,
    0, 0, 0, 0, 103, 39, 59, 144, 0, 0, 0, 0, 103, 205, 102, 160, 0, 0, 0, 0, 105,
    7, 29, 144, 0, 0, 0, 0, 105, 173, 72, 160, 0, 0, 0, 0, 106, 230, 255, 144, 0,
    0, 0, 0, 107, 150, 101, 32, 0, 0, 0, 0, 108, 208, 28, 16, 0, 0, 0, 0, 109, 118,
    71, 32, 0, 0, 0, 0, 110, 175, 254, 16, 0, 0, 0, 0, 111, 86, 41, 32, 0, 0, 0,
    0, 112, 143, 224, 16, 0, 0, 0, 0, 113, 54, 11, 32, 0, 0, 0, 0, 114, 111, 194,
    16, 0, 0, 0, 0, 115, 21, 237, 32, 0, 0, 0, 0, 116, 79, 164, 16, 0, 0, 0, 0, 116,
    255, 9, 160, 0, 0, 0, 0, 118, 56, 192, 144, 0, 0, 0, 0, 118, 222, 235, 160, 0,
    0, 0, 0, 120, 24, 162, 144, 0, 0, 0, 0, 120, 190, 205, 160, 0, 0, 0, 0, 121,
    248, 132, 144, 0, 0, 0, 0, 122, 158, 175, 160, 0, 0, 0, 0, 123, 216, 102, 144,
    0, 0, 0, 0, 124, 126, 145, 160, 0, 0, 0, 0, 125, 184, 72, 144, 0, 0, 0, 0, 126,
    94, 115, 160, 0, 0, 0, 0, 127, 152, 42, 144, 5, 1, 2, 1, 2, 3, 4, 2, 1, 2, 1,
    2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
    2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
    2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
    2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
    2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
    2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
    2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 255, 255, 145, 38, 0,
    0, 255, 255, 157, 144, 1, 4, 255, 255, 143, 128, 0, 8, 255, 255, 157, 144, 1,
    12, 255, 255, 157, 144, 1, 16, 255, 255, 143, 128, 0, 8, 76, 77, 84, 0, 80, 68,
    84, 0, 80, 83, 84, 0, 80, 87, 84, 0, 80, 80, 84, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
    0, 1, 1, 10, 80, 83, 84, 56, 80, 68, 84, 44, 77, 51, 46, 50, 46, 48, 44, 77,
    49, 49, 46, 49, 46, 48, 10,
  ]
  let name = "America/Los_Angeles"
  let zone = Zone::from_tzif2(name, data)
  inspect(zone, content="America/Los_Angeles")
  let current_time_seconds = 1706659200L // Tuesday, January 30, 2024 4:00:00pm PST
  let got = unix(current_time_seconds, zone~)
  inspect(got, content="2024-01-30T16:00:00-08:00[PST]")
  inspect(got.offset(), content="-08:00")
  inspect(got.offset().is_dst(), content="false")
  let current_time_seconds = 1754006400L // Thursday, July 31, 2025 5:00:00pm PDT
  let got = unix(current_time_seconds, zone~)
  inspect(got, content="2025-07-31T17:00:00-07:00[PDT]")
  inspect(got.offset(), content="-07:00")
  inspect(got.offset().is_dst(), content="true")
  // Behavior of a fixed-offset zone:
  let zone = fixed_zone(name, -25200, abbrev="PDT", dst=true)
  let got = unix(current_time_seconds, zone~)
  inspect(got, content="2025-07-31T17:00:00-07:00[PDT]")
  inspect(got.offset(), content="-07:00")
  inspect(got.offset().is_dst(), content="true")
}
